package elicecn.media.vox;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class VoxDecode {
	public final static int VOX_NR_STEPS = 49;
	public final static short[] VOX_STEP_SIZE = { 16, 17, 19, 21, 23, 25, 28,
			31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130,
			143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449,
			494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411,
			1552 };
	public final static short[] VOX_STEP_ADJUST = { -1, -1, -1, -1, 2, 4, 6, 8 };

	/**
	 * 转换VOX文件为WAVE文件。输入为6000Hz/4bitVOX，输出为6000Hz/8bitWAVE。其中，VOX没有文件头，为纯数据内容。
	 * 
	 * @param inVoxFile
	 * @param outWavFile
	 * @throws IOException
	 */
	public static void convertFile(String inVoxFile, String outWavFile)
			throws IOException {
		short[] wavSamples = convertFile(inVoxFile);
		byte[] header = new WaveHeader(wavSamples.length).getBytes();
		FileOutputStream fos = new FileOutputStream(outWavFile);
		writeWaveStream(fos, header, wavSamples);
	}

	public static void convertFile(String inVoxFile, OutputStream os)
			throws IOException {
		short[] wavSamples = convertFile(inVoxFile);
		byte[] header = new WaveHeader(wavSamples.length * 2).getBytes();
		writeWaveStream(os, header, wavSamples);
	}

	/**
	 * 将VOX文件转换为WAVE采样
	 * 
	 * @param inVoxFile
	 * @return WAVE采样
	 * @throws IOException
	 */
	protected static short[] convertFile(String inVoxFile) throws IOException {
		File in = new File(inVoxFile);
		int voxBytes = (int) in.length();
		int voxSamples = voxBytes * 2;// VOX采样数
		short[] wavSamples = new short[voxSamples];// Wave采样

		int wavCount = 0;
		byte[] bytes = new byte[voxBytes];
		BufferedInputStream is = new BufferedInputStream(
				new FileInputStream(in));
		is.read(bytes);
		is.close();
		VoxWave voxWave = new VoxWave((byte) 0, (short) 0, (short) 0, (short) 0);
		for (byte b : bytes) {
			voxWave.voxSample = (byte) ((b >> 4) & 0x0f);
			voxWave = decode(voxWave);
			wavSamples[wavCount++] = voxWave.wavSample;
			voxWave.voxSample = (byte) (b & 0x0f);
			voxWave = decode(voxWave);
			wavSamples[wavCount++] = voxWave.wavSample;
		}
		return wavSamples;
	}

	/**
	 * 输出6000Hz/8bit Wave音频数据
	 * 
	 * @param outWav
	 * @param wavSamples
	 * @throws IOException
	 */
	protected static void writeWaveStream(OutputStream outWav, byte[] header,
			short[] wavSamples) throws IOException {
		BufferedOutputStream os = new BufferedOutputStream(outWav);
		os.write(header);
		os.flush();
		for (short sample : wavSamples) {
			byte[] b = { (byte) ((((sample >>> 4) & 0xff) + 0x80) & 0xff) };
			os.write(b);
		}
		os.flush();
		os.close();
	}

	/**
	 * 对输入的Vox采样进行解码
	 * 
	 * @see VoxDecode#decode(byte, short, short)
	 * @param voxWave
	 * @return VoxWave
	 */
	private static VoxWave decode(VoxWave voxWave) {
		return decode(voxWave.voxSample, voxWave.voxCurrentStep,
				voxWave.wavLastSample);
	}

	/**
	 * 对输入的Vox采样进行解码
	 * 
	 * @param voxSample
	 *            Vox采样 低4位
	 * @param voxCurrentStep
	 *            当前步进
	 * @param wavLastSample
	 *            上一个Wav采样 低12位
	 * @return VoxWave
	 */
	protected static VoxWave decode(byte voxSample, short voxCurrentStep,
			short wavLastSample) {
		assert ((voxSample & 0x0f) == voxSample);
		assert ((wavLastSample & 0x7f) == wavLastSample);
		assert (voxCurrentStep >= 0);
		assert (voxCurrentStep <= 48);

		// d(n) = (ss(n)*B2)+(ss(n)/2*B1)+(ss(n)/4*B0)+(ss(n)/8)
		// if(B3 = 1)
		// then d(n) = d(n) * (-1)
		short stepSize = VOX_STEP_SIZE[voxCurrentStep];
		short offset = (short) (stepSize / 8);
		if ((voxSample & 0x01) != 0) {
			offset += stepSize / 4;
		}
		if ((voxSample & 0x02) != 0) {
			offset += stepSize / 2;
		}
		if ((voxSample & 0x04) != 0) {
			offset += stepSize;
		}
		if ((voxSample & 0x08) != 0) {
			offset *= -1;
		}
		// X(n) = X(n-1) + d(n)
		short wavSample = (short) (wavLastSample + offset);
		// adjust the ret to be 12 bits
		if (wavSample > 2047) {
			wavSample = 2047;
		} else if (wavSample < -2048) {
			wavSample = -2048;
		}
		//
		wavLastSample = wavSample;
		voxCurrentStep += VOX_STEP_ADJUST[voxSample & 0x07];
		// voxCurrentStep *= 1.1 * VOX_STEP_ADJUST[voxSample & 0x07];
		if (voxCurrentStep < 0) {
			voxCurrentStep = 0;
		} else if (voxCurrentStep > VOX_NR_STEPS - 1) {
			voxCurrentStep = VOX_NR_STEPS - 1;
		}
		return new VoxWave(voxSample, voxCurrentStep, wavLastSample, wavSample);
	}

	public static void main(String[] args) throws IOException {
		File folder = new File("D:\\dev\\[BONC]\\vox\\file");
		for (File file : folder.listFiles()) {
			if (file.isFile()) {
				String filename = file.getCanonicalPath();
				if (filename.indexOf(".") == -1) {
					VoxDecode.convertFile(filename, filename + "-"
							+ System.currentTimeMillis() + ".wav");
				}
			}
		}
		System.out.println("done!");
	}
}
